package com.freedom.auth.interfaces.controller;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.freedom.auth.infrastructure.constant.AuthCacheConstant;
import com.freedom.auth.infrastructure.utils.AuthUtil;
import com.freedom.common.core.constant.SecurityConstants;
import com.freedom.common.core.enums.AuthResultCodeEnum;
import com.freedom.common.core.result.CommonResult;
import com.freedom.common.framework.util.JwtUtils;
import com.freedom.common.redis.service.RedisService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
import java.util.Map;
import java.util.Objects;

@Api(tags = "认证中心")
@RestController
@RequestMapping("/oauth")
@AllArgsConstructor
@Slf4j
public class AuthController {

    private TokenEndpoint tokenEndpoint;

    private RedisService redisService;

    @ApiOperation(value = "OAuth2认证", notes = "登录入口")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "grant_type", defaultValue = "password", value = "授权模式", required = true),
            @ApiImplicitParam(name = "client_id", defaultValue = "client", value = "Oauth2客户端ID", required = true),
            @ApiImplicitParam(name = "client_secret", defaultValue = "123456", value = "Oauth2客户端秘钥", required = true),
            @ApiImplicitParam(name = "refresh_token", value = "刷新token"),
            @ApiImplicitParam(name = "username", defaultValue = "admin", value = "用户名"),
            @ApiImplicitParam(name = "password", defaultValue = "123456", value = "用户密码")
    })
    @PostMapping("/token")
    public Object postAccessToken(@ApiIgnore Principal principal, @ApiIgnore @RequestParam Map<String, String> parameters, HttpServletRequest httpServletRequest) throws HttpRequestMethodNotSupportedException {
        /**
         * 获取登录认证的客户端ID
         *
         * 兼容两种方式获取Oauth2客户端信息（client_id、client_secret）
         * 方式一：client_id、client_secret放在请求路径中(注：当前版本已废弃)
         * 方式二：放在请求头（Request Headers）中的Authorization字段，且经过加密，例如 Basic Y2xpZW50OnNlY3JldA== 明文等于 client:secret
         */
        String clientId = AuthUtil.getAuthClientId();
        log.info("OAuth认证授权 客户端ID:{}，请求参数：{}", clientId, JSONUtil.toJsonStr(parameters));

        /**
         * knife4j接口文档测试使用
         *
         * 请求头自动填充，token必须原生返回，不能有任何包装，否则显示 undefined undefined
         * 账号/密码:  client_id/client_secret : client/123456
         */
//        if (SecurityConstants.TEST_CLIENT_ID.equals(clientId)) {
//            memberAuthPublish.loginEventPublisher(parameters, httpServletRequest);
//            return tokenEndpoint.postAccessToken(principal, parameters).getBody();
//        }
        OAuth2AccessToken accessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
        return CommonResult.success(accessToken);
    }

    @ApiOperation(value = "注销")
    @DeleteMapping("/logout")
    public CommonResult<?> logout() {
        JSONObject payload = JwtUtils.getJwtPayload();
        // JWT唯一标识
        String jti = payload.getStr(SecurityConstants.JWT_JTI);
        // JWT过期时间戳(单位：秒)
        Long expireTime = payload.getLong(SecurityConstants.JWT_EXP);
        if (Objects.nonNull(expireTime)) {
            // 当前时间（单位：秒）
            long currentTime = System.currentTimeMillis() / 1000;
            // token未过期，添加至缓存作为黑名单限制访问，缓存时间为token过期剩余时间
            if (expireTime > currentTime) {
                redisService.set(String.format(AuthCacheConstant.TOKEN_BLACKLIST_PREFIX, jti), null, (expireTime - currentTime));
            }
        } else { // token 永不过期则永久加入黑名单
            redisService.set(String.format(AuthCacheConstant.TOKEN_BLACKLIST_PREFIX, jti), null);
        }
        return CommonResult.success(AuthResultCodeEnum.LOG_OUT_SUCCESS);
    }

}
